﻿using System.Collections.Generic;
using System.Linq;
using ContactCenter.Core.Common;
using ContactCenter.Core.ObjectModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using StructureMap;

namespace ContactCenter.Core.Tests
{
    [TestClass]
    public class StructureMapContainerFixture
    {
        private IContainer _container;

        [TestInitialize]
        public void Setup()
        {
            //Arrange 
            _container = new Container();
        }

        //Act
        private void RegisterBootstrapperTasks()
        {
            _container.Configure(c => c.Scan(s =>
                                                 {
                                                     s.AddAllTypesOf(typeof (IBootstrapperJob));
                                                     s.ModifyGraphAfterScan(
                                                         x =>
                                                         x.PluginFamilies.Each(
                                                             y => y.SetScopeTo(InstanceScope.Singleton)));
                                                     s.AssemblyContainingType(typeof(StructureMapContainerFixture));
                                                 }));
        }

        //Act
        private void RegisterConfigurationSteps()
        {
            _container.Configure(c => c.Scan(s =>
                                                 {
                                                     s.ConnectImplementationsToTypesClosing(
                                                         typeof (IConfigurationTask<>));
                                                     s.ModifyGraphAfterScan(
                                                         x =>
                                                         x.PluginFamilies.Each(
                                                             y => y.SetScopeTo(InstanceScope.Singleton)));
                                                     s.AssemblyContainingType(typeof(StructureMapContainerFixture));
                                                 }));
        }

        //Assert
        [TestMethod]
        public void BootstrapperTasksHasSingletonLifeCycle()
        {
            RegisterBootstrapperTasks();

            IList<IBootstrapperJob> tasks1 = _container.GetAllInstances<IBootstrapperJob>();

            IList<IBootstrapperJob> tasks2 = _container.GetAllInstances<IBootstrapperJob>();

            Assert.AreEqual(2, tasks1.Count);

            for (int i = 0; i < tasks1.Count; i++)
            {
                Assert.AreEqual(tasks1[i], tasks2[i]);
                Assert.AreSame(tasks1[i], tasks2[i]);
            }
        }

        [TestMethod]
        public void BootstrapperTasksIsInContainer()
        {
            RegisterBootstrapperTasks();

            IList<IBootstrapperJob> tasks = _container.GetAllInstances<IBootstrapperJob>();

            Assert.AreEqual(2, tasks.Count());
        }

        [TestMethod]
        public void BootstrapperTasksHasCorrectOrder()
        {
            RegisterBootstrapperTasks();

            IBootstrapperJob[] jobs = _container.GetAllInstances<IBootstrapperJob>().OrderBy(x => x.Position).ToArray();

            Assert.AreEqual(2, jobs.Length);
            Assert.IsInstanceOfType(jobs[0], typeof (SecondBootstrapperJob));
            Assert.IsInstanceOfType(jobs[1], typeof (FirstBootstrapperJob));
        }

        [TestMethod]
        public void ConfigurationStepsHasSingletonLifeCycle()
        {
            RegisterConfigurationSteps();

            IList<IConfigurationTask<Configuration>> steps1 =
                _container.GetAllInstances<IConfigurationTask<Configuration>>();
            IList<IConfigurationTask<Configuration>> steps2 =
                _container.GetAllInstances<IConfigurationTask<Configuration>>();

            Assert.AreEqual(2, steps1.Count);
            Assert.AreEqual(steps1.Count, steps2.Count);

            for (int i = 0; i < steps1.Count; i++)
            {
                Assert.AreEqual(steps1[i], steps2[i]);
                Assert.AreSame(steps1[i], steps2[i]);
            }
        }

        [TestMethod]
        public void ConfigurationStepsIsInContainer()
        {
            RegisterConfigurationSteps();

            IList<IConfigurationTask<Configuration>> steps =
                _container.GetAllInstances<IConfigurationTask<Configuration>>();

            Assert.AreEqual(2, steps.Count());
        }

        [TestMethod]
        public void ConfigurationStepsHasCorrectOrder()
        {
            RegisterConfigurationSteps();

            IConfigurationTask<Configuration>[] tasks =
                _container.GetAllInstances<IConfigurationTask<Configuration>>().OrderBy(x => x.Position).ToArray();

            Assert.AreEqual(2, tasks.Length);
            Assert.IsInstanceOfType(tasks[0], typeof (FirstConfigurationTask));
            Assert.IsInstanceOfType(tasks[1], typeof (SecondConfigurationTask));
        }

        [TestMethod]
        public void ConfigurationStepCanRegisterOtherComponentsInContainer()
        {
            RegisterConfigurationSteps();

            IConfigurationTask<Configuration>[] tasks =
                _container.GetAllInstances<IConfigurationTask<Configuration>>().OrderBy(x => x.Position).ToArray();

            tasks.ForEach(x => x.Configure(new Configuration()));

            var service1 = _container.GetInstance<IService>();
            var service2 = _container.GetInstance<IService>();

            Assert.IsNotNull(service1);
            Assert.IsNotNull(service2);
            Assert.AreNotEqual(service1, service2);
            Assert.AreNotSame(service1, service2);
        }

        #region Nested type: Configuration

        public class Configuration
        {
        }

        #endregion

        #region Nested type: FakeService

        public class FakeService : IService
        {
        }

        #endregion

        #region Nested type: FirstBootstrapperTask

        public class FirstBootstrapperJob : IBootstrapperJob
        {
            #region IBootstrapperTask Members

            public virtual int Position
            {
                get { return 2; }
            }

            public void Execute()
            {
            }

            #endregion
        }

        #endregion

        #region Nested type: FirstConfigurationStep

        public class FirstConfigurationTask : IConfigurationTask<Configuration>
        {
            public bool CanConfigure()
            {
                return true;
            }

            #region IConfigurationStep<Configuration> Members

            public virtual Configuration Configure(Configuration configuration)
            {
                return configuration;
            }

            public virtual int Position
            {
                get { return 1; }
            }

            #endregion
        }

        #endregion

        #region Nested type: IService

        public interface IService
        {
        }

        #endregion

        #region Nested type: SecondBootstrapperTask

        public class SecondBootstrapperJob : FirstBootstrapperJob
        {
            public override int Position
            {
                get { return 1; }
            }
        }

        #endregion

        #region Nested type: SecondConfigurationStep

        public class SecondConfigurationTask : FirstConfigurationTask
        {
            private readonly IContainer _container;

            public SecondConfigurationTask(IContainer container)
            {
                _container = container;
            }

            public override int Position
            {
                get { return 2; }
            }

            public override Configuration Configure(Configuration configuration)
            {
                _container.Configure(c => c.For<IService>().Use<FakeService>());

                return configuration;
            }
        }

        #endregion
    }
}